package ace.module.stock.core.impl.service.impl;

import ace.cmp.util.AceAssertUtils;
import ace.module.common.api.model.input.TenantAndAppTypeInput;
import ace.module.stock.api.enums.WmsStockRecordActionTypeEnum;
import ace.module.stock.api.model.dto.WmsStockDto;
import ace.module.stock.api.model.dto.ext.WmsStockDetailDto;
import ace.module.stock.api.model.input.WmsBizInitInput;
import ace.module.stock.api.model.input.WmsStockActionRecordInput;
import ace.module.stock.api.model.input.WmsStockActionRecordWithBatchSnInput;
import ace.module.stock.api.model.input.WmsStockActionTypeRecordInput;
import ace.module.stock.api.model.input.WmsStockActionTypeRecordWithBatchSnInput;
import ace.module.stock.api.model.input.WmsStockReverseBatchSnInput;
import ace.module.stock.core.impl.dao.aggregate.WmsStockAggregate;
import ace.module.stock.core.impl.dao.aggregate.helper.WmsStockRecordAggregateHelper;
import ace.module.stock.core.impl.dao.entity.WmsApplication;
import ace.module.stock.core.impl.dao.entity.WmsStock;
import ace.module.stock.core.impl.dao.entity.WmsStockBatchRecord;
import ace.module.stock.core.impl.dao.entity.WmsStockRecord;
import ace.module.stock.core.impl.manager.WmsApplicationManager;
import ace.module.stock.core.impl.manager.WmsStockBatchRecordManager;
import ace.module.stock.core.impl.manager.WmsStockManager;
import ace.module.stock.core.impl.manager.WmsStockRecordManager;
import ace.module.stock.core.impl.service.WmsStockService;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

/**
 * .
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class WmsStockServiceImpl implements WmsStockService {

  private final WmsApplicationManager wmsApplicationManager;
  private final WmsStockManager wmsStockManager;
  private final WmsStockBatchRecordManager wmsStockBatchRecordManager;
  private final WmsStockRecordManager wmsStockRecordManager;

  private final WmsStockAggregate wmsStockAggregate;
  private final WmsStockRecordAggregateHelper wmsStockRecordAggregateHelper;

  @Override
  public WmsStockDetailDto initOrSet(TenantAndAppTypeInput<WmsBizInitInput> input) {
    WmsApplication wmsApplication = wmsApplicationManager.getAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());

    wmsStockManager.initOrSet(wmsApplication, input.getData());

    return this.getDetail(input.getTenantId(), input.getType(), input.getData().getObjectParentId());
  }

  @Override
  public String add(TenantAndAppTypeInput<WmsStockActionRecordWithBatchSnInput> input) {
    WmsApplication wmsApplication = this.wmsApplicationManager.getAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    List<WmsStockActionRecordInput> wmsStockActionRecordInputs = input.getData().getActionRecordInputs();
    String batchSn = this.wmsStockBatchRecordManager.getDefaultSnOrNewSn(input.getData().getBatchSn());
    return this.addSkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, wmsStockActionRecordInputs);
  }

  @Override
  public String subtract(TenantAndAppTypeInput<WmsStockActionRecordWithBatchSnInput> input) {
    WmsApplication wmsApplication = this.wmsApplicationManager.getAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    List<WmsStockActionRecordInput> wmsStockActionRecordInputs = input.getData().getActionRecordInputs();
    String batchSn = this.wmsStockBatchRecordManager.getDefaultSnOrNewSn(input.getData().getBatchSn());
    return this.subtractSkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, wmsStockActionRecordInputs);
  }

  @Override
  public String modify(TenantAndAppTypeInput<WmsStockActionTypeRecordWithBatchSnInput> input) {
    WmsApplication wmsApplication = this.wmsApplicationManager.getAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    List<WmsStockActionTypeRecordInput> wmsStockActionTypeRecordInputs = input.getData().getActionTypeRecordInputs();
    String batchSn = this.wmsStockBatchRecordManager.getDefaultSnOrNewSn(input.getData().getBatchSn());
    return this.modifySkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, wmsStockActionTypeRecordInputs);
  }

  @Override
  public String reverse(TenantAndAppTypeInput<WmsStockReverseBatchSnInput> input) {
    WmsApplication wmsApplication = this.wmsApplicationManager.getAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    String batchSn = input.getData().getBatchSn();
    return this.reverse0(wmsApplication, batchSn);
  }

  @Override
  public WmsStockDetailDto getDetail(Long tenantId, String appType, Long objectParentId) {
    Long wmsAppId = this.wmsApplicationManager.getIdByOutIdAndOutType(tenantId.toString(), appType);
    if (Objects.isNull(wmsAppId)) {
      return null;
    }
    WmsStockDto parentStockDto = this.wmsStockManager.getParentStockDto(wmsAppId, objectParentId);
    List<WmsStockDto> childrenStockDto = this.wmsStockManager.getChildrenStockDtos(wmsAppId, objectParentId);
    return WmsStockDetailDto.builder()
        .stock(parentStockDto)
        .children(childrenStockDto)
        .build();
  }

  @Override
  public List<WmsStockDto> getSkuByObjectIdIn(TenantAndAppTypeInput<List<Long>> input) {
    WmsApplication wmsApplication = wmsApplicationManager.getByOutIdAndOutType(input.getTenantId().toString(), input.getType());

    if (Objects.isNull(wmsApplication)) {
      return new ArrayList<>();
    }

    return wmsStockManager.getSkuDtoByWmsAppIdAndObjectIdIn(wmsApplication.getId(), input.getData());
  }

  @Override
  public List<WmsStockDto> getProductByObjectIdIn(TenantAndAppTypeInput<List<Long>> input) {
    WmsApplication wmsApplication = wmsApplicationManager.getByOutIdAndOutType(input.getTenantId().toString(), input.getType());

    if (Objects.isNull(wmsApplication)) {
      return new ArrayList<>();
    }

    return wmsStockManager.getProductDtoByObjectIdIn(wmsApplication.getId(), input.getData());
  }

  private String subtractSkuStockAndAutoCalcRecursionParentStocks(WmsApplication wmsApplication, String batchSn, List<WmsStockActionRecordInput> wmsStockActionRecordInputs) {
    return this.addOrSubtractSkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, WmsStockRecordActionTypeEnum.SUBTRACT, wmsStockActionRecordInputs);
  }

  private String addSkuStockAndAutoCalcRecursionParentStocks(WmsApplication wmsApplication, String batchSn, List<WmsStockActionRecordInput> wmsStockActionRecordInputs) {
    return this.addOrSubtractSkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, WmsStockRecordActionTypeEnum.ADD, wmsStockActionRecordInputs);
  }

  private String addOrSubtractSkuStockAndAutoCalcRecursionParentStocks(
      WmsApplication wmsApplication,
      String batchSn,
      WmsStockRecordActionTypeEnum actionTypeEnum,
      List<WmsStockActionRecordInput> wmsStockActionRecordInputs) {
    AceAssertUtils.isFalse(
        WmsStockRecordActionTypeEnum.ADD.equals(actionTypeEnum) || WmsStockRecordActionTypeEnum.SUBTRACT.equals(actionTypeEnum),
        "仅支持增加与扣减操作"
    );

    List<WmsStockActionTypeRecordInput> skuStockActionRecordWithTypeInputs = this.convertWmsStockActionRecordWithTypeInputs(wmsStockActionRecordInputs, actionTypeEnum);

    this.modifySkuStockAndAutoCalcRecursionParentStocks(wmsApplication, batchSn, skuStockActionRecordWithTypeInputs);

    return batchSn;
  }

  private String modifySkuStockAndAutoCalcRecursionParentStocks(WmsApplication wmsApplication, String batchSn, List<WmsStockActionTypeRecordInput> skuStockActionRecordWithTypeInputs) {
    AceAssertUtils.isEmpty(skuStockActionRecordWithTypeInputs, "库存操作不能为空");
    AceAssertUtils.isNull(wmsApplication, "仓库应用不能为空");
    AceAssertUtils.isEmpty(batchSn, "操作批次流水号不能为空");

    Long wmsAppId = wmsApplication.getId();

    List<Long> skuObjectIdInputs = skuStockActionRecordWithTypeInputs.stream()
        .map(WmsStockActionRecordInput::getObjectId)
        .collect(Collectors.toList());

    List<WmsStock> skuStockAndAllRecursionParentStocks = this.wmsStockManager.getSkuStockAndAllRecursionParentStocksByWmsAppIdAndObjectIdIn(wmsAppId, skuObjectIdInputs);

    this.wmsStockManager.checkNotContainsAllWmsStockByObjectId(skuStockAndAllRecursionParentStocks, skuObjectIdInputs);

    List<WmsStock> skuWmsStocks = this.wmsStockManager.getSkuStocksFromList(skuStockAndAllRecursionParentStocks);

    this.wmsStockBatchRecordManager.checkNotExistBatchSnByWmsApIdAndBatchSn(wmsAppId, batchSn);

    List<WmsStockRecord> skuWmsStockRecords = this.wmsStockRecordManager.buildModifyRecord(wmsAppId, batchSn, skuWmsStocks, skuStockActionRecordWithTypeInputs);

    WmsStockBatchRecord wmsStockBatchRecord = this.wmsStockBatchRecordManager.build(wmsAppId, batchSn);

    this.wmsStockAggregate.modifySkuStockAndCalcRecursionParentStocks(skuStockAndAllRecursionParentStocks, wmsStockBatchRecord, skuWmsStockRecords);

    return batchSn;
  }

  private List<WmsStockActionTypeRecordInput> convertWmsStockActionRecordWithTypeInputs(List<WmsStockActionRecordInput> wmsStockActionRecordInputs, WmsStockRecordActionTypeEnum actionTypeEnum) {
    return wmsStockActionRecordInputs.stream()
        .map(wmsStockActionRecordInput -> {
          return WmsStockActionTypeRecordInput.builder()
              .actionType(actionTypeEnum.getCode())
              .quantity(wmsStockActionRecordInput.getQuantity())
              .objectId(wmsStockActionRecordInput.getObjectId())
              .build();
        })
        .collect(Collectors.toList());
  }

  /**
   * 回退增加与扣减操作库存.
   *
   * @param wmsApplication 仓库应用
   * @param batchSn 操作批次流水号
   * @return 回退操作批次流水号, 或空字符串,空字符串代表没有需要还原的操作
   */
  private String reverse0(WmsApplication wmsApplication, String batchSn) {
    AceAssertUtils.isEmpty(batchSn, "操作批次流水号不能为空");
    Long wmsAppId = wmsApplication.getId();
    List<WmsStockRecord> wmsStockRecords = this.wmsStockRecordManager.getByWmsAppIdAndBatchSnAndReverseFlagIsNoneAndActionTypeInAddSubtract(wmsAppId, batchSn);
    if (CollectionUtils.isEmpty(wmsStockRecords)) {
      return StringUtils.EMPTY;
    }
    String reverseBatchSn = this.wmsStockBatchRecordManager.getNewSn();
    WmsStockBatchRecord wmsStockBatchRecord = this.wmsStockBatchRecordManager.build(wmsAppId, reverseBatchSn);
    List<WmsStockRecord> reverseWmsStockRecords = this.wmsStockRecordManager.buildAddOrSubtractReverseRecord(reverseBatchSn, wmsStockRecords);
    List<Long> skuWmsStockIds = this.wmsStockRecordAggregateHelper.getStockIdsFromList(reverseWmsStockRecords);
    List<WmsStock> skuStockAndAllRecursionParentStocks = this.wmsStockManager.getSkuStockAndAllRecursionParentStocksByWmsAppIdAndSkuWmsStockIdIn(wmsAppId, skuWmsStockIds);
    this.wmsStockAggregate.reverseAndCalcRecursionParentStocks(skuStockAndAllRecursionParentStocks, wmsStockBatchRecord, reverseWmsStockRecords);
    return reverseBatchSn;
  }
}